﻿//MarketQuotingStrategy.cs
//Copyright (c) 2013 StockSharp LLC, all rights reserved.
//This code module is part of StockSharp library.
//This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3.
//See the file License.txt for the license details.
//More info on: http://stocksharp.com

namespace StockSharp.Algo.Strategies
{
	using System;
	using System.Linq;

	using Ecng.Collections;

	using StockSharp.BusinessEntities;

	/// <summary>
	/// Котирование по рыночной цене.
	/// </summary>
	public class MarketQuotingStrategy : BestByPriceQuotingStrategy
	{
		/// <summary>
		/// Создать <see cref="MarketQuotingStrategy"/>.
		/// </summary>
		/// <param name="quotingDirection">Направление котирования.</param>
		/// <param name="quotingVolume">Объем, который необходимо скотировать.</param>
		public MarketQuotingStrategy(OrderDirections quotingDirection, decimal quotingVolume)
			: base(quotingDirection, quotingVolume)
		{
			_priceType = this.Param("PriceType", MarketPriceTypes.Following);
			_priceOffset = this.Param("PriceOffset", new Unit());
		}

		/// <summary>
		/// Создать <see cref="MarketQuotingStrategy"/>.
		/// </summary>
		/// <param name="order">Заявка, которую необходимо котировать.</param>
		/// <param name="bestPriceOffset">Отступ от лучшей цены, на которую может уйти котируемая заявка.</param>
		/// <param name="priceOffset">Отступ цены для выставляемой заявки. Определяет размер отступа от лучшей котировки (для покупки прибавляется к цене, для продажи - вычитается).</param>
		/// <returns>Стратегия.</returns>
		public MarketQuotingStrategy(Order order, Unit bestPriceOffset, Unit priceOffset)
			: base(order, bestPriceOffset)
		{
			_priceType = this.Param("PriceType", MarketPriceTypes.Following);
			_priceOffset = this.Param("PriceOffset", priceOffset);
		}

		private readonly StrategyParam<MarketPriceTypes> _priceType;

		/// <summary>
		/// Тип рыночной цены. По умолчанию равен <see cref="MarketPriceTypes.Following"/>.
		/// </summary>
		public MarketPriceTypes PriceType
		{
			get { return _priceType.Value; }
			set { _priceType.Value = value; }
		}

		private readonly StrategyParam<Unit> _priceOffset;

		/// <summary>
		/// Отступ цены для выставляемой заявки. Определяет размер отступа от лучшей котировки (для покупки прибавляется к цене, для продажи - вычитается).
		/// </summary>
		public Unit PriceOffset
		{
			get { return _priceOffset.Value; }
			set { _priceOffset.Value = value; }
		}

		/// <summary>
		/// Получить лучшую цену. Если невозможно вычислить лучшую цену на данный момент, то будет возвращено 0.
		/// </summary>
		protected override decimal BestPrice
		{
			get
			{
				Unit newPrice;
				Unit offset;

				switch (PriceType)
				{
					case MarketPriceTypes.Opposite:
						var oppositeDir = QuotingDirection.Invert();

						var quotes = Security
								.GetMarketDepth()
								.GetQuotes(oppositeDir)
								.GetFilteredQuotes(QuotingOrders);

						if (!quotes.IsEmpty())
						{
							newPrice = QuotingDirection == OrderDirections.Buy ? quotes.Min(q => q.Price) : quotes.Max(q => q.Price);
							newPrice.Security = Security;
						}
						else
							newPrice = Security.GetCurrentPrice(oppositeDir);

						offset = -PriceOffset;

						break;
					case MarketPriceTypes.Following:
						// использовать только стаканные цены,
						// так как цена последней сделки может выдавать неправильные значения
						//newPrice = base.BestPrice;
						var quote = FilteredQuotes.FirstOrDefault();
						newPrice = quote != null ? quote.Price : 0;
						newPrice.Security = Security;
						offset = PriceOffset;
						break;
					case MarketPriceTypes.Middle:
						newPrice = Security.GetCurrentPrice(QuotingDirection, MarketPriceTypes.Middle, QuotingOrders);
						offset = null;
						break;
					default:
						throw new ArgumentOutOfRangeException();
				}

				if (offset != null)
				{
					if (newPrice == 0)	// PYH: Нулевая цена значит вообще нет данных никаких.
						return 0;

					newPrice = newPrice.ApplyOffset(QuotingDirection, offset);
				}

				return (decimal)newPrice;
			}
		}
	}
}
